#include <algorithm>
#include <iostream>
#include <math.h>
#include <stdio.h>

#include "city.hpp"
#include "font.hpp"
#include "foreach.hpp"
#include "hex_geometry.hpp"
#include "model.hpp"
#include "occupation.hpp"
#include "random.hpp"
#include "raster.hpp"
#include "string_utils.hpp"
#include "terrain.hpp"
#include "terrain_improvement.hpp"
#include "tile.hpp"
#include "world.hpp"

namespace hex {

tile::tile(const location& loc, const std::string& str)
  : loc_(loc), terrain_(terrain::get(str)), seed_(rand())
{
	std::fill(roads_, roads_ + sizeof(roads_)/sizeof(*roads_), 0);
	if(!terrain_) {
		std::cerr << "no such terrain: '" << str << "'\n";
		throw parse_error();
	}

	for(int i = 0; i != 2; ++i) {
		citizens_.push_back(citizen_ptr(new citizen));
	}

	center_.position[0] = tile_translate_x(loc);
	center_.position[1] = tile_translate_y(loc);
	center_.position[2] = 0;
	center_.init = true;

	for(int n = 0; n != 6; ++n) {
		calculate_corner(n);
	}
}

tile::~tile()
{}

void tile::set_heightmap(heightmap& heights) const
{
	//find the upper-left location
	const int xpos = 3*loc().x();
	const int ypos = 2*loc().y() + ((loc().x()%2) == 0 ? 0 : 1);

	std::vector<int> v = terrain_->generate_heights();
	if(v.empty()) {
		return;
	}

	int pos = 0;
	for(int y = 0; y != 3; ++y) {
		for(int x = 0; x != 5 && pos < v.size(); ++x, ++pos) {
			heights.add(xpos + x, ypos + y, v[pos]/1000.0);
		}
	}
}

namespace {

const GLfloat UVTop = 12.0/128.0;
const GLfloat UVBot = 118.0/128.0;
const GLfloat UVHeight = UVBot - UVTop;
const GLfloat UVLeftTip = 6.0/128.0;
const GLfloat UVRightTip = 128.0/128.0;
const GLfloat UVWidth = UVRightTip - UVLeftTip;

const GLfloat UVVerticalMargin = 10.0/128.0;
const GLfloat UVHorizontalMargin = 33.0/128.0;
const GLfloat UVCenter[] = {0.5,0.5};
const GLfloat UVCorners[6][2] = {
	{1.0-UVHorizontalMargin,1.0-UVVerticalMargin},
	{1.0,0.5},
	{1.0-UVHorizontalMargin,UVVerticalMargin + 2.0/128.0},
	{UVHorizontalMargin + 6.0/128.0,UVVerticalMargin + 2.0/128.0},
	{6.0/128.0,0.5},
	{UVHorizontalMargin + 6.0/128.0,1.0-UVVerticalMargin}
};

// function to set the uv given a heightmap position within a hex.
void set_uv(const heightmap& heights, int x, int y, const graphics::texture& t)
{
	if(x < 0) {
		x = (4 << heights.subdivisions()) + x;
	}
	if(y < 0) {
		y = (2 << heights.subdivisions()) + y;
	}
	const GLfloat uv_x = UVLeftTip + (GLfloat(x)/(4 << heights.subdivisions()))*UVWidth;
	const GLfloat uv_y = UVTop + (GLfloat(y)/(2 << heights.subdivisions()))*UVHeight;
	t.set_coord(uv_x, uv_y);
}

}

void tile::draw(const heightmap& heights_param) const
{
	const graphics::texture& t = terrain_->get_texture(loc_);
	if(!t.valid()) {
		return;
	}
	draw(heights_param, terrain_->get_texture(loc_), 0.0);

	if(terrain_->is_water()) {
		draw_water();
	}
}

void tile::draw(const heightmap& heights_param, const graphics::texture& t, GLfloat height_adj) const
{
	const heightmap* heights_ptr = &heights_param;
	if(terrain_->detail_level() != -1 && terrain_->detail_level() < heights_ptr->subdivisions()) {
		heights_ptr = heights_ptr->lower_detail_version(terrain_->detail_level());
	}

	assert(heights_ptr);

	const heightmap& heights = *heights_ptr;

	//find the upper-left location
	const int subdiv = 1 << heights.subdivisions();
	const int xpos = 3*subdiv*loc().x();
	const int ypos = 2*subdiv*loc().y() + ((loc().x()%2) == 0 ? 0 : subdiv);
	const int ypos2 = 2*subdiv*(loc().y()+1) + ((loc().x()%2) == 0 ? 0 : subdiv);

	t.set_as_current_texture();

	const GLfloat translate_x = tile_translate_x(loc_);
	const GLfloat translate_y = tile_translate_y(loc_);
	const GLfloat translate_y2 = translate_y + 1.0;

	for(int y = 0; y != 1 + heights.points_between(); ++y) {
		const int xstart = heights.points_between() - y;
		const int xend = xstart + 4 + 2*heights.points_between() + y*2;
		assert(xend > xstart);

		// Draw the side at the top half of the hex
		glBegin(GL_TRIANGLE_STRIP);
		for(int x = xstart; x != xend; ++x) {
			if(x != xstart) {
				set_uv(heights, x, y, t);
				heights.gl_normal(xpos + x, ypos + y);
				glVertex3f(translate_x + x*0.25/subdiv, translate_y + y*0.5/subdiv, heights.val(xpos + x, ypos + y) + height_adj);
			}
			set_uv(heights, x, y + 1, t);
			heights.gl_normal(xpos + x, ypos + y + 1);
			glVertex3f(translate_x + x*0.25/subdiv, translate_y + (y+1)*0.5/subdiv, heights.val(xpos + x, ypos + y + 1) + height_adj);
		}
		glEnd();

		const int x = xend - 1;

		//the one left over triangle on the end.
		glBegin(GL_TRIANGLES);
		set_uv(heights, x, y, t);
		heights.gl_normal(xpos + x, ypos + y);
		glVertex3f(translate_x + x*0.25/subdiv, translate_y + y*0.5/subdiv, heights.val(xpos + x, ypos + y) + height_adj);
		set_uv(heights, x, y + 1, t);
		heights.gl_normal(xpos + x, ypos + y + 1);
		glVertex3f(translate_x + x*0.25/subdiv, translate_y + (y+1)*0.5/subdiv, heights.val(xpos + x, ypos + y + 1) + height_adj);
		set_uv(heights, x + 1, y + 1, t);
		heights.gl_normal(xpos + x + 1, ypos + y + 1);
		glVertex3f(translate_x + (x+1)*0.25/subdiv, translate_y + (y+1)*0.5/subdiv, heights.val(xpos + x + 1, ypos + y + 1) + height_adj);
		glEnd();

		// Now the side at the bottom half.
		glBegin(GL_TRIANGLE_STRIP);
		for(int x = xstart; x != xend; ++x) {
			if(x != xstart) {
				set_uv(heights, x, -y, t);
				heights.gl_normal(xpos + x, ypos2 - y);
				glVertex3f(translate_x + x*0.25/subdiv, translate_y2 - y*0.5/subdiv, heights.val(xpos + x, ypos2 - y) + height_adj);
			}
			set_uv(heights, x, -y - 1, t);
			heights.gl_normal(xpos + x, ypos2 - y - 1);
			glVertex3f(translate_x + x*0.25/subdiv, translate_y2 - (y+1)*0.5/subdiv, heights.val(xpos + x, ypos2 - y - 1) + height_adj);
		}
		glEnd();

		//the one left over triangle on the end.
		glBegin(GL_TRIANGLES);
		set_uv(heights, x, -y, t);
		heights.gl_normal(xpos + x, ypos2 - y);
		glVertex3f(translate_x + x*0.25/subdiv, translate_y2 - y*0.5/subdiv, heights.val(xpos + x, ypos2 - y) + height_adj);
		set_uv(heights, x, -y - 1, t);
		heights.gl_normal(xpos + x, ypos2 - y - 1);
		glVertex3f(translate_x + x*0.25/subdiv, translate_y2 - (y+1)*0.5/subdiv, heights.val(xpos + x, ypos2 - y - 1) + height_adj);
		set_uv(heights, x + 1, -y - 1, t);
		heights.gl_normal(xpos + x + 1, ypos2 - y - 1);
		glVertex3f(translate_x + (x+1)*0.25/subdiv, translate_y2 - (y+1)*0.5/subdiv, heights.val(xpos + x + 1, ypos2 - y - 1) + height_adj);
		glEnd();
	}

	if(!citizens_.empty()) {
		rng rng(seed());
		int positions_taken[15];
		int npositions = 0;
		foreach(const const_citizen_ptr& c, citizens_) {
			if(npositions >= 12) {
				break;
			}

			int pos = rng.generate()%15;
			while(std::count(positions_taken, positions_taken + npositions, pos) != 0) {
				pos = (pos+2)%15;
			}

			positions_taken[npositions++] = pos;

			GLfloat xpos = 0.25;
			if(pos >= 5) {
				xpos += 0.25;
				if(pos >= 10) {
					xpos += 0.25;
				}
			}

			c->draw(rng, translate_x+xpos, translate_y+0.1+((pos%5)*0.15), 0.0);
		}
	}

	//now draw any models that are present.
	if(terrain_->num_models()) {
		rng gen(seed());
		for(int n = 0; n != terrain_->num_models(); ++n) {
			GLfloat x = GLfloat(gen.generate()%1000)/1000.0;
			const GLfloat y = GLfloat(gen.generate()%1000)/1000.0;
			const GLfloat ydiff = fabs(0.5 - y);
			x = ydiff/2.0 + x*(1.0 - ydiff);
			graphics::const_model_ptr m = terrain_->get_model(gen);

			glPushMatrix();
			glTranslatef(translate_x + x, translate_y + y, 0.0);
			glRotatef(GLfloat(gen.generate()%(314*2))/100.0, 0.0, 0.0, 1.0);
			m->draw();
			glPopMatrix();
		}
	}


/*
	t.set_coord(UVCenter[0], UVCenter[1]);
	draw_point(center_);

	for(int i = 0; i != 7; ++i) {
		const int n = i%6;
		t.set_coord(UVCorners[n][0], UVCorners[n][1]);
		draw_point(corners_[n]);
	}

	glEnd();
	glEnable(GL_TEXTURE_2D);
	const int pixel_x = tile_translate_x(loc_);
	const int pixel_y = tile_translate_y(loc_);
	if(terrain_->underlying()) {
		graphics::blit_texture(terrain_->underlying()->get_texture(loc_), pixel_x, pixel_y);
	}
	graphics::blit_texture(terrain_->get_texture(loc_), pixel_x, pixel_y);

	const int resource_x = pixel_x + HexWidth - 10;
	int resource_y = pixel_y + 10;
//	foreach(const resource_instance& r, resources_) {
		char buf[20];
		sprintf(buf, "%d", citizen::rate_tile(*this)); //r.quality);
		graphics::blit_texture(graphics::font::render_text(buf, 14, graphics::color_black()), resource_x, resource_y);
		resource_y += 20;
//	}

	if(city_) {
		city_->draw();
	}

	int citizens_x = pixel_x + HexSize - HexWidth;
	int citizens_y = pixel_y + 10;
	foreach(const const_citizen_ptr& c, citizens_) {
		c->draw(citizens_x, citizens_y);
		citizens_y += 6;
		if(citizens_y > pixel_y + 40) {
			citizens_x = pixel_x + HexSize - HexWidth + 12;
			citizens_y = pixel_y + 10;
		}
	}

	foreach(const resource_instance& r, resources_) {
		r.resource->draw(r.quality, pixel_x, pixel_y);
	}

	if(improvement_) {
		improvement_->draw(pixel_x, pixel_y);
	}
*/
}

void tile::draw_water() const
{
	const graphics::texture t = graphics::texture::get("textures/seamlesswater.jpg");
	t.set_as_current_texture();
	const GLfloat translate_x = tile_translate_x(loc_);
	const GLfloat translate_y = tile_translate_y(loc_);
	const GLfloat sea_level = -0.5;
	const GLfloat scale_pan = 0.5;
	glBegin(GL_TRIANGLE_FAN);

	glNormal3f(0.0, 0.0, 1.0);
	GLfloat x = translate_x + 0.5;
	GLfloat y = translate_y + 0.5;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 0.25;
	y = translate_y;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 0.75;
	y = translate_y;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 1.0;
	y = translate_y + 0.5;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 0.75;
	y = translate_y + 1.0;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 0.25;
	y = translate_y + 1.0;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x;
	y = translate_y + 0.5;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	x = translate_x + 0.25;
	y = translate_y;
	t.set_coord(x*scale_pan, y*scale_pan);
	glVertex3f(x, y, sea_level);

	glEnd();
}

void tile::draw_infrastructure() const
{
	for(int i = 0; i != 3; ++i) {
		if(!rivers_[i]) {
			continue;
		}

		int x1, y1, x2, y2;
		tile_corner_pixel_xy(loc_, static_cast<DIRECTION>(i), &x1, &y1);
		tile_corner_pixel_xy(loc_, static_cast<DIRECTION>(i+1), &x2, &y2);

		glDisable(GL_TEXTURE_2D);
		glLineWidth(4);
		glBegin(GL_LINES);
		glColor4f(0.0, 0.0, 0.5, 1.0);
		
		glVertex3f(x1, y1, 0);
		glVertex3f(x2, y2, 0);
		glColor4f(1.0, 1.0, 1.0, 1.0);
		glEnd();
		glEnable(GL_TEXTURE_2D);
		glLineWidth(1);
	}

	const int src_pixel_x = tile_pixel_x(loc_) + HexSize/2;
	const int src_pixel_y = tile_pixel_y(loc_) + HexSize/2;

	if(std::count(roads_, roads_ + 3, 0) == 3) {
		return;
	}

	glDisable(GL_TEXTURE_2D);
	glLineWidth(8);
	glBegin(GL_LINES);
	glColor4f(0.3, 0.3, 0.0, 1.0);

	for(int n = 0; n != 3; ++n) {
		if(!roads_[n]) {
			continue;
		}
		const location dst = tile_in_direction(loc_, static_cast<DIRECTION>(n));
		const int dst_pixel_x = tile_pixel_x(dst) + HexSize/2;
		const int dst_pixel_y = tile_pixel_y(dst) + HexSize/2;

		glVertex3f(src_pixel_x, src_pixel_y, 0);
		glVertex3f(dst_pixel_x, dst_pixel_y, 0);
	}
	glColor4f(1.0, 1.0, 1.0, 1.0);

	glEnd();
	glEnable(GL_TEXTURE_2D);
	glLineWidth(1);
}

void tile::draw_borders(const world& w) const
{
	const const_government_ptr o = owner();
	if(!o) {
		return;
	}
	location adj[6];
	get_adjacent_tiles(loc(), adj);
	for(int n = 0; n != 6; ++n) {
		if(!w.map().is_loc_on_map(adj[n])) {
			continue;
		}
		const_tile_ptr t = w.map().get_tile(adj[n]);
		if(t->owner() == o) {
			continue;
		}

		DIRECTION dir = static_cast<DIRECTION>(n);
		int x1, y1, x2, y2;
		tile_corner_pixel_xy(loc_, static_cast<DIRECTION>(n), &x1, &y1);
		tile_corner_pixel_xy(loc_, static_cast<DIRECTION>((n+1)%6), &x2, &y2);
		
		glDisable(GL_TEXTURE_2D);
		glLineWidth(3);
		glBegin(GL_LINES);
		const SDL_Color& color = o->color();
		glColor4ub(color.r, color.g, color.b, 128);
		
		glVertex3f(x1, y1, 0);
		glVertex3f(x2, y2, 0);
		glColor4f(1.0, 1.0, 1.0, 1.0);
		glEnd();
		glEnable(GL_TEXTURE_2D);
		glLineWidth(1);
	}
}

void tile::draw_description() const
{
	int pixel_x = tile_pixel_x(loc_);
	int pixel_y = tile_pixel_y(loc_);
	foreach(const_citizen_ptr c, citizens_) {
			graphics::blit_texture(graphics::font::render_text(c->text_description(), 14, graphics::color_white()), pixel_x, pixel_y);
			pixel_y += 18;
	}

	if(city_) {
		std::string desc = city_->description();
		std::vector<std::string> lines = util::split(desc, '\n');
		foreach(const std::string& line, lines) {
			graphics::blit_texture(graphics::font::render_text(line, 14, graphics::color_white()), pixel_x, pixel_y);
			pixel_y += 18;
		}
	}
}

void tile::add_resource(const std::string& name, int quality)
{
	if(quality < 2) {
		return;
	}

	resource_instance instance;
	instance.quality = quality;
	instance.resource = resource::get(name);
	if(!instance.resource) {
		std::cerr << "Unknown resource: '" << name << "'\n";
		return;
	}

	foreach(resource_instance& i, resources_) {
		if(i.resource == instance.resource) {
			i = instance;
			return;
		}
	}

	resources_.push_back(instance);
}

namespace {
struct citizen_survives {
public:
	explicit citizen_survives(std::vector<std::string>* death_reasons)
	    : death_reasons_(death_reasons)
	{}

	bool operator()(const citizen_ptr& c) {
		std::string reason;
		if(c->survives(&reason)) {
			return true;
		} else {
			death_reasons_->push_back(reason);
			return false;
		}
	}
private:
	std::vector<std::string>* death_reasons_;
};

bool citizen_reproduces(const citizen_ptr& c) {
	return c->reproduces();
}
}

void tile::process_occupations(world& w)
{
	foreach(const citizen_ptr& c, citizens_) {
		if(c->get_occupation()) {
			const bool result = c->get_occupation()->do_work(w, *this, *c);
			if(!result) {
				//quit job
				c->change_occupation(NULL);
			}
		}
	}
}

void tile::end_turn(const world& w)
{
	process_deaths_and_births(w);
	process_cultural_conversions();
	foreach(const citizen_ptr& c, citizens_) {
		c->process_item_spoilage();
	}

	std::vector<resource_instance> resources_backup = resources_;

	foreach(const citizen_ptr& c, citizens_) {
		if(c->has_job() || c->get_occupation()) {
			continue;
		}

		static const int NumProduction = 3;
		resource_instance* produced_resources[NumProduction];
		for(int n = 0; n != NumProduction; ++n) {
			const resource* r = c->produce(*this);
			produced_resources[n] = NULL;
			if(r != NULL) {
				const int roll = rand()%6 + rand()%6 + 2;
				resource_instance* instance = NULL;
				foreach(resource_instance& i, resources_) {
					if(i.resource.get() == r) {
						instance = &i;
					}
				}

				if(instance != NULL) {
					if(std::count(produced_resources, produced_resources + n, instance) == 0) {
						produced_resources[n] = instance;
					}
					const int hurdle = (instance->quality + c->production_bonus(instance->resource) + instance->resource->weather_bonus(w.weather(), 6 - std::count(rivers_, rivers_ + 6, river_ptr())));
					if(roll <= hurdle) {
						c->get_item(instance->resource->produces(), instance->resource->production_rate());
						if(instance->resource->uses().empty() == false) {
							c->get_item(instance->resource->uses(), -instance->resource->production_rate());
						}
					}
				} else {
					c->get_item(r->produces(), r->production_rate());
					if(r->uses().empty() == false) {
						c->get_item(r->uses(), -r->production_rate());
					}
				}
			}
		}

		for(int n = 0; n != NumProduction; ++n) {
			if(produced_resources[n]) {
				produced_resources[n]->quality--;
			}
		}

		//if we only produced from one resource, then see if the citizen
		//promotes.
		if(std::count(produced_resources, produced_resources + NumProduction, (resource_instance*)NULL) == NumProduction - 1 && produced_resources[0]) {
			const_resource_ptr r = produced_resources[0]->resource;
			if(r->specialization().empty() == false && r->level_up() && r->level_up()->execute(*c).as_bool()) {
				c->level_up(r);
			}
		}
	}

	resources_.swap(resources_backup);
}

void tile::process_deaths_and_births(const world& w)
{
	causes_of_deaths_.clear();
	citizen_survives survive_fn(&causes_of_deaths_);
	std::vector<citizen_ptr>::iterator deaths = std::partition(citizens_.begin(), citizens_.end(), survive_fn);

	std::map<std::string, int> inheritance;
	for(std::vector<citizen_ptr>::iterator i = deaths; i != citizens_.end(); ++i) {
		if((*i)->has_job()) {
			(*i)->job()->set_positions_taken((*i)->job()->positions_taken() - 1);
			(*i)->job()->set_positions_available((*i)->job()->positions_available() + 1);
		}

		(*i)->all_items(inheritance);
	}

	const int survivors = deaths - citizens_.begin();
	int heir = 0;
	while(survivors && !inheritance.empty()) {
		while(!inheritance.empty() && inheritance.begin()->second <= 0) {
			inheritance.erase(inheritance.begin());
		}

		if(inheritance.empty()) {
			break;
		}

		citizens_[heir%survivors]->get_item(inheritance.begin()->first, 1);
		inheritance.begin()->second--;
		++heir;
	}

	citizens_.erase(deaths, citizens_.end());

	const int ncitizens = citizens_.size();
	for(int n = 0; n != ncitizens; ++n) {
		if(citizen_reproduces(citizens_[n])) {
			citizen_ptr baby(new citizen);
			baby->set_allegiance(citizens_[n]->allegiance());
			std::map<std::string, int> items;
			citizens_[n]->all_items(items);
			for(std::map<std::string, int>::const_iterator i = items.begin();
			    i != items.end(); ++i) {
				const int transfer = i->second/2;
				baby->get_item(i->first, transfer);
				citizens_[n]->get_item(i->first, -transfer);
			}
			citizens_.push_back(baby);
		}
	}
}

void tile::process_cultural_conversions()
{
	foreach(const citizen_ptr& a, citizens_) {
		if(!a->allegiance()) {
			continue;
		}

		foreach(const citizen_ptr& b, citizens_) {
			if(a->allegiance() != b->allegiance()) {
				a->cultural_conversion(*b);
			}
		}
	}
}

void tile::migrate_to(tile& o)
{
	assert(!citizens_.empty());
	o.citizens_.push_back(citizens_.back());
	citizens_.pop_back();
}

void tile::place_orders(barter_market& mkt)
{
	foreach(const citizen_ptr& c, citizens_) {
		c->place_orders(mkt);
	}
}

int tile::move_cost() const
{
	return terrain_->move_cost();
}

void tile::set_city(city_ptr c) { city_ = c; resources_.clear(); }
city_ptr tile::city() { return city_; }
const_city_ptr tile::city() const { return city_; }

void tile::build_road(hex::DIRECTION dir)
{
	roads_[dir]++;
}

void tile::add_river(hex::DIRECTION dir, river_ptr river)
{
	rivers_[dir] = river;
}

void tile::clear_influences()
{
	influences_.clear();
}

void tile::add_influence(city_ptr c)
{
	influences_.push_back(c);
}

const_government_ptr tile::owner() const
{
	if(influences_.empty()) {
		return const_government_ptr();
	}

	return influences_.front()->owner();
}

const_terrain_improvement_ptr tile::can_build_improvement() const
{
	if(improvement_) {
		return const_terrain_improvement_ptr();
	}

	foreach(const resource_instance& r, resources_) {
		const std::string& id = r.resource->improvement();
		if(id.empty() == false) {
			const_terrain_improvement_ptr result = terrain_improvement::get(id);
			if(result) {
				return result;
			}
		}
	}

	return const_terrain_improvement_ptr();
}

void tile::build_improvement(const_terrain_improvement_ptr improvement)
{
	improvement_ = improvement;
}

variant tile::get_value(const std::string& key) const
{
	if(key == "city") {
		if(city_) {
			return variant(city_.get());
		} else {
			return variant();
		}
	} else if(key == "market") {
		if(influences_.empty() == false) {
			return variant(&influences_.front()->market());
		} else {
			return variant();
		}
	} else if(key == "ncitizens") {
		return variant(citizens_.size());
	} else {
		foreach(const resource_instance& r, resources_) {
			if(r.resource->produces() == key && (r.resource->improvement().empty() || improvement_ && improvement_->id() == r.resource->improvement())) {
				return variant(r.quality + r.resource->weather_bonus(0, 6 - std::count(rivers_, rivers_ + 6, river_ptr())));
			}
		}

		return variant(0);
	}
}

void tile::draw_point(const tile::point& p) const
{
	glVertex3fv(p.position);
}

void tile::calculate_corner(int n)
{
	const DIRECTION neighbor_a = static_cast<DIRECTION>(n);
	const DIRECTION neighbor_b = static_cast<DIRECTION>((n+1)%6);

	location adj[6];
	get_adjacent_tiles(loc_, adj);
	corners_[n].position[0] = (tile_translate_x(loc_) +
	                           tile_translate_x(adj[neighbor_a]) +
	                           tile_translate_x(adj[neighbor_b]))/3.0;
	corners_[n].position[1] = (tile_translate_y(loc_) +
	                           tile_translate_y(adj[neighbor_a]) +
	                           tile_translate_y(adj[neighbor_b]))/3.0;
	corners_[n].position[2] = 0.0;
	corners_[n].init = true;
}

}
